
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;

/**
 * This panel contains UI elements that enable the user to search for a 
 * particular showing based on certain constraints as well as purchase a 
 * ticket for a selected showing.
 * @author Chris, Travis
 */
public class UserShowingsPanel extends JPanel {
	private static final long serialVersionUID = 1250061013277123453L;

	// results of query
	private ArrayList<ShowingRow> showing_list;

	// table and scrollpane to hold results of query
	private JScrollPane scrolly;
	private JTable table;
	
	// database connection info
	private final DatabaseInfo db_info;

	// checkboxes for indicating to use a search parameter
	private JCheckBox chckbxMovie;
	private JCheckBox chckbxCinema;
	private JCheckBox chckbxTime;
	private JCheckBox chckbxGenre;
	private JCheckBox chckbxRating;
	
	// hold options for each search parameter type
	private JComboBox<String> timeBox;
	private JComboBox<String> timeBox2;
	private JComboBox<String> movieBox;
	private JComboBox<String> cinemaBox;
	private JComboBox<String> genreBox;
	private JComboBox<String> ratingBox;
	
	// arrays to store values returned from query
	private String[] movie_titles;
	private String[] movie_genres;
	private String[] cinema_names;
	private String[] movie_ratings;
	
	// time values to be used in the query
	private final int[] time_nums = new int[]{0,100,200,300,400,500,600,700,800,
		900,1000,1100,1200,1300,1400,1500,1600,1700,1800,1900,2000,2100,2200,
		2300};
	
	// time values to show to user
	private final String[] time_strings = new String[]{"Midnight", "1:00 AM",
			"2:00 AM", "3:00 AM", "4:00 AM", "5:00 AM", "6:00 AM", "7:00 AM", 
			"8:00 AM", "9:00 AM", "10:00 AM", "11:00 AM", "Noon", "1:00 PM",
			"2:00 PM", "3:00 PM", "4:00 PM", "5:00 PM", "6:00 PM", "9:00 PM",
			"10:00 PM", "11:00 PM"};

	/**
	 * Create the panel.
	 */
	public UserShowingsPanel(DatabaseInfo the_db_info) {
		db_info = the_db_info;
		createAndShowGUI();
	}
	
	/**
	 * Creates all the GUI elements that the user sees.
	 */
	private void createAndShowGUI() {
		setLayout(null);
		
		chckbxMovie = new JCheckBox("Movie:");
		chckbxMovie.setFont(new Font("Tahoma", Font.PLAIN, 17));
		chckbxMovie.setBounds(30, 20, 83, 29);
		add(chckbxMovie);
		
		chckbxCinema = new JCheckBox("Cinema:");
		chckbxCinema.setFont(new Font("Tahoma", Font.PLAIN, 17));
		chckbxCinema.setBounds(30, 52, 100, 29);
		add(chckbxCinema);
		
		chckbxTime = new JCheckBox("Time:");
		chckbxTime.setFont(new Font("Tahoma", Font.PLAIN, 17));
		chckbxTime.setBounds(30, 92, 83, 29);
		add(chckbxTime);
		
		chckbxGenre = new JCheckBox("Genre:");
		chckbxGenre.setFont(new Font("Tahoma", Font.PLAIN, 17));
		chckbxGenre.setBounds(30, 131, 83, 29);
		add(chckbxGenre);
		
		chckbxRating = new JCheckBox("Rating:");
		chckbxRating.setFont(new Font("Tahoma", Font.PLAIN, 17));
		chckbxRating.setBounds(330, 131, 80, 29);
		add(chckbxRating);

		movie_titles = popluateComboBox(db_info, "Movie");
		movieBox = new JComboBox<String>(movie_titles);
		movieBox.setBounds(131, 27, 215, 20);
		movieBox.addActionListener(new CombineRadioListener());
		movieBox.setName("Movie");
		add(movieBox);
		
		cinema_names = popluateComboBox(db_info, "Cinema");
		cinemaBox = new JComboBox<String>(cinema_names);
		cinemaBox.setBounds(131, 61, 270, 20);
		cinemaBox.addActionListener(new CombineRadioListener());
		cinemaBox.setName("Cinema");
		add(cinemaBox);
		
		timeBox = new JComboBox<String>(time_strings);
		timeBox.setBounds(131, 99, 97, 20);
		timeBox.addActionListener(new CombineRadioListener());
		timeBox.setName("Time");
		add(timeBox);
		
		JLabel lblTo = new JLabel("To:");
		lblTo.setBounds(238, 102, 21, 14);
		add(lblTo);
		
		timeBox2 = new JComboBox<String>(time_strings);
		timeBox2.setBounds(262, 99, 97, 20);
		timeBox2.addActionListener(new CombineRadioListener());
		timeBox2.setName("Time");
		add(timeBox2);
		
		movie_genres = popluateComboBox(db_info, "Genre");
		genreBox = new JComboBox<String>(movie_genres);
		genreBox.setBounds(131, 138, 130, 20);
		genreBox.addActionListener(new CombineRadioListener());
		genreBox.setName("Genre");
		add(genreBox);
		
		movie_ratings = popluateComboBox(db_info, "Rating");
		ratingBox = new JComboBox<String>(movie_ratings);
		ratingBox.setBounds(420, 138, 178, 20);
		ratingBox.addActionListener(new CombineRadioListener());
		ratingBox.setName("Rating");
		add(ratingBox);
		
		JLabel lblChooseAFilm = new JLabel("Choose a Movie:");
		lblChooseAFilm.setFont(new Font("Segoe UI", Font.PLAIN, 9));
		lblChooseAFilm.setBounds(131, 11, 101, 14);
		add(lblChooseAFilm);
		
		JLabel lblChooseA = new JLabel("Choose a Cinema:");
		lblChooseA.setFont(new Font("Segoe UI", Font.PLAIN, 9));
		lblChooseA.setBounds(131, 46, 101, 14);
		add(lblChooseA);
		
		JLabel lblChooseATime = new JLabel("Choose a Time:");
		lblChooseATime.setFont(new Font("Segoe UI", Font.PLAIN, 9));
		lblChooseATime.setBounds(131, 82, 101, 14);
		add(lblChooseATime);
		
		JLabel lblChooseAGenre = new JLabel("Choose a Genre:");
		lblChooseAGenre.setFont(new Font("Segoe UI", Font.PLAIN, 9));
		lblChooseAGenre.setBounds(131, 123, 101, 14);
		add(lblChooseAGenre);
		
		JLabel lblChooseARating = new JLabel("Choose a Rating:");
		lblChooseARating.setFont(new Font("Segoe UI", Font.PLAIN, 9));
		lblChooseARating.setBounds(430, 123, 90, 14);
		add(lblChooseARating);
		
		JButton btnStartSearch = new JButton("Start Search");
		btnStartSearch.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				refreshTable();	
			}
		});
		btnStartSearch.setBounds(650, 137, 112, 23);
		add(btnStartSearch);
		
		createAddScrollPane(new JTable());
		
		JButton btnPurchase = new JButton("Purchase");
		btnPurchase.setBounds(650, 336, 112, 23);
		btnPurchase.addActionListener(new PurchaseListener());
		add(btnPurchase);
		
		JLabel label = new JLabel("Find Showings");
		label.setFont(new Font("Tahoma", Font.BOLD, 26));
		label.setBounds(480, 15, 200, 40);
		add(label);
		
		JLabel label2 = new JLabel("Find Movie Times and Buy Tickets");
		label2.setFont(new Font("Tahoma", Font.ITALIC, 10));
		label2.setBounds(540, 35, 250, 40);
		add(label2);

		refreshTable();
	}
	
	private void createAddScrollPane(JTable table) {
		scrolly = new JScrollPane(table);
		scrolly.setBounds(10,167,761,165);
		add(scrolly);
	}
	
	/**
	 * Gets all the values of a particular attribute
	 * @param dbi The database info to use
	 * @param search_type The attribute being searched for
	 * @return The values available for the given attribute.
	 */
	private String[] popluateComboBox(DatabaseInfo dbi, String search_type) {
		Db db = Db.getDb();
		String[] tuple = null;
		String query = null;
		if (search_type.equals("Movie")) 
			query = "SELECT movies.title FROM movies";
		else if (search_type.equals("Cinema")) 
			query = "SELECT cinemas.cName FROM cinemas";
		else if (search_type.equals("Genre")) 
			query = "SELECT DISTINCT(movies.genre) FROM movies";
		else if (search_type.equals("Rating")) 
			query = "SELECT DISTINCT(movies.rating) FROM movies";
		
		String result = null;
		
		try {
			result = db.executeQuery2(dbi.server, dbi.database, query, 
					dbi.user_name, dbi.password);
		} catch (Exception e1) {
			System.out.println(e1.getMessage());
			e1.printStackTrace();
		}
		
		if(result != null) 
			tuple = result.split("@");
		
		return tuple;
	}
	
	/**
	 * Executes a given query and returns the results;
	 * @param query The query to execute;
	 * @return The result of the query. Returns <code>null</code> if there
	 * 			was an exception thrown.
	 */
	private String getQuery(String query) {
		Db db = Db.getDb();
		String result = null;
		
		try {
			result = db.executeQuery(db_info.server, db_info.database, query, 
					db_info.user_name, db_info.password);
		} catch (Exception e1) {
			System.out.println(e1.getMessage());
			e1.printStackTrace();
		}
		
		return result;
	}

	/**
	 * This method reads in the states of all checkboxes on the panel and
	 * the selected values of the comboboxes they correspond to to form
	 * an SQL query with the desired properties.
	 * @return
	 */
	private String formQuery() {
		String base = "SELECT cName, movies.title, genre, rating, time FROM movies, showings WHERE movies.title = showings.title ";
		
		String movie = null;
		String cinema = null;
		String startTime = null;
		String endTime = null;
		String genre = null;
		String rating = null;

		if(chckbxMovie.isSelected()) 
			movie = movie_titles[movieBox.getSelectedIndex()];
		if(chckbxTime.isSelected()) {
			int st = time_nums[timeBox.getSelectedIndex()];
			int et = time_nums[timeBox2.getSelectedIndex()];
			startTime = String.valueOf(time_nums[timeBox.getSelectedIndex()]);
			if(et > st)
				endTime = String.valueOf(et);
		}
		if(chckbxGenre.isSelected()) 
			genre = movie_genres[genreBox.getSelectedIndex()];
		if(chckbxCinema.isSelected()) 
			cinema = cinema_names[cinemaBox.getSelectedIndex()];
		if(chckbxRating.isSelected())
			rating = movie_ratings[ratingBox.getSelectedIndex()];
		
		StringBuilder sb = new StringBuilder();
		sb.append(base);

		if(null != movie) 
			sb.append(" AND showings.title='" + movie + "' ");
		if(null != startTime)
			sb.append(" AND time>" + startTime + " ");
		if(null != endTime) 
			sb.append(" AND time<" + endTime + " ");
		if(null != genre) 
			sb.append(" AND genre='" + genre + "' ");
		if(null != cinema)
			sb.append(" AND showings.cName='" + cinema + "' ");
		if(null != rating) 
			sb.append(" AND movies.rating='" + rating + "'");
		
		sb.append(";");
		return sb.toString();
	}
	
	private ArrayList<String> splitRowStrings(String the_data) {
		ArrayList<String> ret = new ArrayList<String>();
		
		while(0 < the_data.length()) {
			int io = the_data.indexOf('\r');
			String s;
			if(0 > io)
				s = the_data.trim();
			else
				s = the_data.substring(0,io).trim();
			
			ret.add(s);
			
			if(0 > io)
				break;
			the_data = the_data.substring(io,the_data.length()).trim();
		}
		return ret;
	}
	
	private String[] splitString(String header_string) {
		header_string = header_string.trim();
		String[] temp = header_string.split("\t");
		
		int valid_count = 0;
		for(int i = 0 ; i < temp.length ; i++) {
			temp[i] = temp[i].trim();
			if(0 < temp[i].length())
				valid_count++;
		}
		
		String[] ret = new String[valid_count];
		int j = 0;
		for(int i = 0 ; i < temp.length ; i++) {
			if(0 < temp[i].length())
				ret[j++] = temp[i];
		}
		return ret;
	}
	
	/**
	 * Converts strings like 'cName' to 'Cinema'
	 * @param str The string to be converted.
	 * @return The converted string.
	 */
	private String convertTitle(final String str) {
		if(str.equals("cName")) return "Cinema";
		else if(str.equals("title")) return "Movie";
		else if(str.equals("time")) return "Time";
		else if(str.equals("rating")) return "Rating";
		else if(str.equals("genre")) return "Genre";
		
		return str; //default
	}
	
	private void refreshTable() {
		final int column_count = 5;
		
		String query = formQuery();
		String result = getQuery(query);
		
		if(null != result) {
			String header_line = result.substring(0, result.indexOf('\r'));

			String[] column_names = splitString(header_line);
			for(int i = 0 ; i < column_names.length ; i++)
				column_names[i] = convertTitle(column_names[i]);
			
			result = result.substring(result.indexOf('\r'),result.length()).trim();
			ArrayList<String> row_strings = splitRowStrings(result);
			Object[][] table_data = new Object[row_strings.size()][column_count];
			showing_list = new ArrayList<ShowingRow>();
			
			if (row_strings.isEmpty()) {
				JOptionPane.showMessageDialog(null,
					    "There were no results for your search query!",
					    "Results",
					    JOptionPane.INFORMATION_MESSAGE);
			}
			
			int x = 0, y = 0;
			for(String s : row_strings) {
				x = 0;
				ShowingRow row = new ShowingRow(s);
				showing_list.add(row);
				table_data[y][x++] = row.getCName();
				table_data[y][x++] = row.getTitle();
				table_data[y][x++] = row.getGenre();
				table_data[y][x++] = row.getRating();
				table_data[y][x++] = row.getTimeString();
				y++;
			}
		
	        table = new JTable(table_data, column_names);
	        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
			table.getColumnModel().getColumn(0).setPreferredWidth(250);
			table.getColumnModel().getColumn(1).setPreferredWidth(220);
			table.getColumnModel().getColumn(2).setPreferredWidth(129);
			table.getColumnModel().getColumn(3).setPreferredWidth(72);
			table.getColumnModel().getColumn(4).setPreferredWidth(72);
	        table.setFillsViewportHeight(true);
	        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	        
	        this.remove(scrolly);
	        createAddScrollPane(table);
		}
	}
	
	/**
	 * This actionlistener allows the user to purchase a ticket for a showing.
	 * @author Chris, Travis
	 */
	private class PurchaseListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			int selected = table.getSelectedRow();
			if(showing_list.size() > selected && -1 < selected) {
				new PurchaseTicketDialog(showing_list.get(selected),
						db_info);
			}
			else if (-1 == selected) {
				JOptionPane.showMessageDialog(null,
					    "You must select a showing before you can purchase a ticket",
					    "Purchase Error",
					    JOptionPane.ERROR_MESSAGE);
			}
		}
	}
	
	/**
	 * This allows us to check a checkbox if an item in the combobox it is
	 * related to is selected.
	 * @author Chris
	 */
	private class CombineRadioListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			@SuppressWarnings("rawtypes")
			JComboBox drop_down = ((JComboBox) e.getSource());
			if (drop_down.getSelectedIndex() > -1) {
				if (drop_down.getName().equals("Movie"))
					chckbxMovie.setSelected(true); 
				else if (drop_down.getName().equals("Cinema"))
					chckbxCinema.setSelected(true); 
				else if (drop_down.getName().equals("Time"))
					chckbxTime.setSelected(true); 
				else if (drop_down.getName().equals("Genre"))
					chckbxGenre.setSelected(true); 
				else if (drop_down.getName().equals("Rating"))
					chckbxRating.setSelected(true); 
			}
		}
	}
}
